package com.carter.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.carter.springframework.beans.factory.*;
import com.carter.springframework.beans.factory.config.*;
import com.carter.springframework.beans.exception.BeanException;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import static cn.hutool.core.util.ClassLoaderUtil.getClassLoader;

/**
 * 抽象的实例化Bean类，提供创建Bean的能力，创建完成后，放入单例bean map中进行缓存
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    private final InstantiationStrategy strategy;

    protected AbstractAutowireCapableBeanFactory(Class<? extends InstantiationStrategy> clazz) {
        try {
            strategy = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeanException("设置实例化策略出错", e);
        }
    }

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
        Object bean;
        try {
            bean = resolveBeforeInstantiation(beanName,beanDefinition);
            if (bean!=null){
                return bean;
            }
            bean = createBeanInstance(beanDefinition, beanName, args);
            //设置bean属性之前，允许beanPostProcessor修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
            //处理循环依赖
            if (beanDefinition.isSingleton()){
                Object finalBean = bean;
                addSingletonFactory(beanName,()->getEarlyBeanReference(beanName, beanDefinition, finalBean));
            }
            //对于返回false的bean，不再执行后续设置bean属性的操作
            boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
            if (!continueWithPropertyPopulation) {
                return bean;
            }
            //设置bean属性之前，允许beanPostProcessor修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName,bean,beanDefinition);
            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean，执行beanPostProcessor的前置和后置方法
            bean = initializeBean(beanName, bean, beanDefinition);

        } catch (Exception e) {
            throw new BeanException("创建bean异常", e);
        }
        //注册实现了DisposableBean接口的对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
        Object exposedObject = bean;
        if (beanDefinition.isSingleton()){
            exposedObject = getSingleton(beanName);
            //创建好的单例bean放入缓存
            addSingleton(beanName, bean);
        }

        return bean;
    }

    /**
     * 对于返回false的bean，不在执行后续设置bean属性的操作
     * @param beanName
     * @param bean
     * @return
     */
    protected boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
        boolean continueWithPropertyPopulation = true;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                if (!((InstantiationAwareBeanPostProcessor) processor).postProcessAfterInstantiation(bean, beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
        return continueWithPropertyPopulation;
    }

    private Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        Object exposedBean = bean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor){
                exposedBean = ((InstantiationAwareBeanPostProcessor) processor).getEarlyBeanReference(exposedBean,beanName);
                if (exposedBean==null){
                    return null;
                }
            }
        }
        return exposedBean;
    }

    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor){
                PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) processor).postProcessPropertyValues(beanDefinition.getPropertyValues(),bean,beanName);
                if (pvs!=null){
                    for (PropertyValue pv : pvs.getPropertyValues()) {
                        beanDefinition.getPropertyValues().addPropertyValue(pv);
                    }
                }
            }
        }
    }

    private Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        Object bean = applyBeanPostProcessorsBeforeInitialization(beanDefinition.getBeanClass(), beanName);
        if (bean!=null){
            bean = applyBeanPostProcessorsAfterInitialization(bean,beanName);
        }
        return bean;
    }

    /**
     * 在需要的情况下，注册销毁方法
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        if (!beanDefinition.isSingleton()){
            return;
        }
        if (bean instanceof DisposableBean||StrUtil.isNotBlank(beanDefinition.getDestroyMethodName())){
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

    /**
     * 初始化bean
     * @param beanName
     * @param bean
     * @param beanDefinition
     * @return
     */
    private Object initializeBean(String beanName,Object bean,BeanDefinition beanDefinition){
        //执行Aware感知操作
        invokeInitMethods(beanName,bean);
        //执行beanPostProcessor before处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization((Class<?>) bean, beanName);
        //执行bean初始化内容
        try {
            invokeInitMethod(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeanException("执行bean初始化方法失败，bean名称：" + beanName);
        }
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    private void invokeInitMethods(String beanName, Object bean) {
        if (bean instanceof Aware){
            if (bean instanceof BeanFactoryAware){
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }
    }

    private void invokeInitMethod(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception{
        //实现了接口InitializingBean的bean，调用接口方法
        if (bean instanceof InitializingBean){
            ((InitializingBean) bean).afterProperties();
        }

        //配置了initMethod的bean，调用初始化方法
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotBlank(initMethodName)){
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            initMethod.invoke(bean);
        }
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Class<?> beanClass, String beanName) throws BeanException {
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor){
                Object resut = ((InstantiationAwareBeanPostProcessor) processor).postProcessBeforeInstantiation(beanClass, beanName);
                if (resut!=null){
                    return resut;
                }
            }
        }
        return null;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeanException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(existingBean, beanName);
            if (null == current) {
                return result;
            }
            result = current;
        }
        return result;
    }

    /**
     * 为生成的bean设置属性
     *
     * @param beanName
     * @param beanDefinition
     * @param bean
     */
    protected void applyPropertyValues(String beanName, BeanDefinition beanDefinition, Object bean) throws BeanException {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue pv : propertyValues.getPropertyValues()) {
            String name = pv.getName();
            Object value = pv.getValue();
            if (value instanceof BeanReference) {
                try {
                    value = getBean(((BeanReference) value).getName());
                } catch (Exception e) {
                    throw new BeanException("设置bean属性时异常" + beanName, e);
                }
            }
            BeanUtil.setFieldValue(bean, name, value);
        }
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws BeanException {
        //拿到所有的构造方法
        Constructor<?>[] declaredConstructors = beanDefinition.getBeanClass().getDeclaredConstructors();
        //开始循环，找到和参数类型一致的方法
        for (Constructor<?> c : declaredConstructors) {
            if (c.getParameterTypes().length == args.length) {
                boolean flag = true;
                for (int i = 0; i < c.getParameterTypes().length; i++) {
                    if (!args[i].getClass().equals(c.getParameterTypes()[i])) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    //todo:调用策略来进行实例化
                    return strategy.instantiate(beanDefinition, beanName, c, args);
                }

            }
        }
        throw new BeanException("创建Bean出错，未找到对应构造方法");
    }


}
